/*  日志器实现
*   1.抽象日志器基类
*   2.同步日志器
*   3.异步日志器
*/

#ifndef _LOGGER_H_
#define _LOGGER_H_

#ifndef _GNU_SOURCE //测试性功能 与POSIX不同
#define _GNU_SOURCE
#endif

#include "format.hpp"
#include "level.hpp"
#include "sink.hpp"
#include "message.hpp"
#include "looper.hpp"
#include <vector>
#include <atomic>
#include <mutex>
#include <string>
#include <memory>
#include <cstdarg>
#include <sstream>
#include <cassert>
#include <functional>
#include <unordered_map>

namespace Log
{
    class Logger
    {
    public:
        Logger(const std::string& logger_name,
            LogLevel::value limit_level,
            Formatter::ptr formatter,
            std::vector<LogSink::ptr>& sinks)
            :_formatter(formatter),_sinks(sinks),_limit_level(limit_level),_logger_name(logger_name) {}

        using ptr = std::shared_ptr<Logger>;

        //获取日志器名称
        const std::string& name() { return _logger_name; }

        //输出日志必须知道 文件名 行号 以及日志主消息(由于日志主消息格式非常多 所以使用不定参传递参数)
        void debug(const std::string& filename,size_t line,const std::string& fmt,...)
        {
            //1.判断日志等级是否可以输出
            if(LogLevel::value::DEBUG < _limit_level) return;
            //2.对不定参数进行组织 形成日志内容
            va_list ls;
            va_start(ls,fmt);
            //3.构造LogMsg对象
            //4.通过formatter格式化LogMsg对象的内容 形成日志字符串
            //5.日志落地

            serialize(LogLevel::value::DEBUG,filename,line,fmt,ls);
        }
        void info(const std::string& filename,size_t line,const std::string& fmt,...)
        {
            if(LogLevel::value::INFO < _limit_level) return;
            va_list ls;
            va_start(ls,fmt);

            serialize(LogLevel::value::INFO,filename,line,fmt,ls);
        }
        void warn(const std::string& filename,size_t line,const std::string& fmt,...)
        {
            if(LogLevel::value::WARN < _limit_level) return;
            va_list ls;
            va_start(ls,fmt);

            serialize(LogLevel::value::WARN,filename,line,fmt,ls);
        }
        void error(const std::string& filename,size_t line,const std::string& fmt,...)
        {
            if(LogLevel::value::ERROR < _limit_level) return;
            va_list ls;
            va_start(ls,fmt);

            serialize(LogLevel::value::ERROR,filename,line,fmt,ls);
        }
        void fatal(const std::string& filename,size_t line,const std::string& fmt,...)
        {
            if(LogLevel::value::FATAL < _limit_level) return;
            va_list ls;
            va_start(ls,fmt);

            serialize(LogLevel::value::FATAL,filename,line,fmt,ls);
        }

    protected:
        //提取不定参数 组织消息 形成LogMsg对象
        //日志等级 文件名称 行号 日志内容主体
        void serialize(LogLevel::value level,const std::string& filename,size_t line,const std::string& fmt,va_list& ls)
        {
            char* res = nullptr;
            int n = vasprintf(&res,fmt.c_str(),ls);
            if(n == -1)
            {
                std::cout<<"fatal vasprintf failed!\n";
                return;
            }
            LogMsg msg(level,line,filename,_logger_name,res);
            std::stringstream ss;
            _formatter->format(ss,msg);
            log(ss.str().c_str(),ss.str().size());
            free(res);
        }

        //日志输出方式 同步异步方式
        virtual void log(const char* data,size_t len) = 0;
    protected:
        //日志格式化对象
        Formatter::ptr _formatter;
        //日志落地模块(因为可以有多个落地方式 以数组的方式管理这些落地方式)
        std::vector<LogSink::ptr> _sinks;
        //日志输出等级(为了保证原子访问 使用atomic)
        std::atomic<LogLevel::value> _limit_level;
        //互斥锁
        std::mutex _mutex;
        //日志器名称
        std::string _logger_name;
    };

    //同步日志器
    class SyncLogger : public Logger
    {
    public:
            SyncLogger(std::string logger_name,
            LogLevel::value limit_level,
            Formatter::ptr formatter,
            std::vector<LogSink::ptr>& sinks)
            :Logger(logger_name,limit_level,formatter,sinks) {}
    protected:
            //重写输出方法
            void log(const char* data,size_t len) override
            {
                //1.加锁
                std::unique_lock<std::mutex> lock(_mutex);
                //2.遍历落地方式 进行输出

                for(auto& sink:_sinks)
                    sink->log(data,len);
            }
    };

    //异步日志器
    class ASyncLogger : public Logger
    {
    public:
            ASyncLogger(std::string logger_name,
            LogLevel::value limit_level,
            Formatter::ptr formatter,
            std::vector<LogSink::ptr>& sinks,
            Async_Type looper_type = Async_Type::ASYNC_SAFE) //异步工作器默认安全
            :Logger(logger_name,limit_level,formatter,sinks)
            ,_looper(new AsyncLooper(std::bind(&ASyncLogger::realLog,this,std::placeholders::_1),looper_type)) {}

            //业务处理 日志落地
            void realLog(Buffer& buf) 
            {
                //if(buf.empty()) return;
                for(auto& sink:_sinks)
                    sink->log(buf.begin(),buf.readAbleSize());

                buf.moveReadPos(buf.readAbleSize());
            }

    protected:
            //重写输出方法 日志处理方式
            void log(const char* data,size_t len) override { _looper->push(data,len); }

    private:
        AsyncLooper::ptr _looper; //异步日志器对象
    };


    //这里用户每次申请日志器需要手动构造大量的小组件，给用户的使用增加了难度
    //这里使用建造者模式建造日志器，简化用户的使用难度，不让用户直接构造日志器

    //1.抽象一个日志器建造者基类(完成日志器所需零件的构造 和 日志器的创建)
    //  a.设置日志器类型
    //  b.将不同的日志器的创建放到同一个日志器建造者类中完成
    //2.派生出具体的建造者类 - 局部日志器建造者 和 全局日志器建造者 (后续设置全局单例管理器后 将日志器添加全局管理)

    //日志器类别
    enum class LoggerType {
        LOGGER_SYNC = 0, //同步日志器
        LOGGER_ASYNC     //异步日志器
    };

    //日志器抽象基类(建造者模式)
    class LoggerBuilder 
    {
    public:
        LoggerBuilder()
        :_logger_type(LoggerType::LOGGER_SYNC)
        ,_limit_level(LogLevel::value::DEBUG)
        ,_looper_type(Async_Type::ASYNC_SAFE) {}
        
        //设置日志器类型(同步/异步)
        void buildLoggerType(LoggerType type) { _logger_type = type; }
        //设置日志器名称
        void buildLoggerName(const std::string& name) { _logger_name = name; }
        //设置日志等级
        void buildLoggerLevel(LogLevel::value limit_level) { _limit_level = limit_level; }
        //设置格式化日志器(传入日志格式化字符串 在函数内部构建格式化日志器)
        void buildFormatter(const std::string& pattern) { _formatter = std::make_shared<Formatter>(pattern); }
        //设置日志落地方法 使用工厂进行创建
        template<class SinkType,class ...Args>
        void buildSink(Args &&...args) { _sinks.push_back(SinkFactor::create<SinkType>(std::forward<Args>(args)...)); }
        //设置异步日志器为非安全模式
        void buildAsyncUnsafe() { _looper_type = Async_Type::ASYNC_UNSAFE; }

        //创建日志器(局部/全局单例)
        virtual Logger::ptr build() = 0;
    protected:
        //日志器类型
        LoggerType _logger_type;
        //异步日志器工作模式(默认安全模式)
        Async_Type _looper_type;
        //日志器名称
        std::string _logger_name;
        //日志输出等级
        LogLevel::value _limit_level;
        //日志格式化器
        Formatter::ptr _formatter;
        //日志落地方法
        std::vector<LogSink::ptr> _sinks;
    };

    //局部日志器
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        //实现局部日志器的创建
        Logger::ptr build() override
        {
            assert(!_logger_name.empty()); //日志器必须有名称 方便管理
            //如果没有日志格式化器 则创建默认的
            if(_formatter.get() == nullptr)
                _formatter = std::make_shared<Formatter>();

            //如果没有日志落地方法 则默认向显示器输出
            if(_sinks.empty())
                buildSink<StdoutSink>();

            //异步日志器
            if(_logger_type == LoggerType::LOGGER_ASYNC)
                return std::make_shared<ASyncLogger>(_logger_name,_limit_level,_formatter,_sinks,_looper_type);

            //输出局部日志器
            return std::make_shared<SyncLogger>(_logger_name,_limit_level,_formatter,_sinks);
        }
    };
    
    //日志器管理器
    class LoggerManager
    {
    public:
        //获取(创建)日志器
        static LoggerManager& getInstance()
        {
            //C++11之后 创建静态局部变量 编译器在编译层面实现了线程安全
            //当一个线程创建静态变量时 其他线程会阻塞在创建语句除
            static LoggerManager eton;
            return eton;
        }

        //添加日志器
        void addLogger(Logger::ptr &logger)
        {
            //如果同名日志器已存在则不允许插入
            if(isLogger(logger->name())) return;

            std::unique_lock<std::mutex> _lock(_mutex);
            _loggers.insert(std::make_pair(logger->name(),logger));
        }

        //判断是否添加了指定日志器
        bool isLogger(const std::string& name)
        {
            std::unique_lock<std::mutex> _lock(_mutex);
            if(_loggers.count(name)) return true;
            return false;
        }

        //获取指定的日志器
        Logger::ptr getLogger(const std::string& name)
        {
            std::unique_lock<std::mutex> _lock(_mutex);
            if(name == "root") return getRootLogger();
            auto it = _loggers.find(name);
            if(it == _loggers.end()) return nullptr;
            return it->second;
        }

        //获取默认日志器
        Logger::ptr getRootLogger() { return _root_logger; }

    private:
        LoggerManager() {
            //这里默认日志器只能创建局部日志器 如果创建全局日志器 会在调用时一直创建默认日志器对象
            std::unique_ptr<LoggerBuilder> logbuild(new LocalLoggerBuilder);
            logbuild->buildLoggerName("root");
            _root_logger = logbuild->build();
        }
        LoggerManager(const LoggerManager&) = delete;
        LoggerManager& operator=(const LoggerManager&) = delete;
    private:
        std::mutex _mutex;
        Logger::ptr _root_logger; //默认日志器
        std::unordered_map<std::string,Logger::ptr> _loggers; //其他日志器
    };

    //全局日志器
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        //实现全局日志器的创建
        Logger::ptr build() override
        {
            assert(!_logger_name.empty()); //日志器必须有名称 方便管理
            //如果没有日志格式化器 则创建默认的
            if(_formatter.get() == nullptr)
                _formatter = std::make_shared<Formatter>();

            //如果没有日志落地方法 则默认向显示器输出
            if(_sinks.empty())
                buildSink<StdoutSink>();

            Logger::ptr logger;
            //异步日志器
            if(_logger_type == LoggerType::LOGGER_ASYNC)
                logger = std::make_shared<ASyncLogger>(_logger_name,_limit_level,_formatter,_sinks,_looper_type);
            else
            //同步日志器
                logger = std::make_shared<SyncLogger>(_logger_name,_limit_level,_formatter,_sinks);

            //加入单例对象管理
            LoggerManager::getInstance().addLogger(logger);
            return logger;
        }
    };
}

#endif